#include "mainwindow.h"
#include <QVBoxLayout>       // 垂直布局
#include <QHBoxLayout>       // 水平布局
#include <QLabel>            // 标签控件
#include <QMessageBox>       // 消息对话框

// 构造函数：初始化主窗口和控件，设置布局，连接信号与槽
MainWindow::MainWindow(QWidget *parent)
        : QMainWindow(parent),
          tcpServer(new QTcpServer(this)), // 创建 TCP 服务器对象
          tcpSocket(nullptr)               // 初始化客户端 socket 指针为空
{
    // 设置中央主控件
    QWidget *central = new QWidget(this);
    setCentralWidget(central);

    // 初始化控件
    comboBoxIp = new QComboBox(this);               // 本机 IP 选择框
    spinBoxPort = new QSpinBox(this);               // 端口号输入框
    spinBoxPort->setMaximum(65535);                 // TCP 最大端口号
    spinBoxPort->setValue(6688);                    // 默认端口设置为 6688

    buttonStart = new QPushButton("启动服务", this); // 启动按钮
    buttonStop = new QPushButton("停止服务", this);   // 停止按钮
    buttonStop->setEnabled(false);                  // 初始时禁用“停止”按钮

    lineEditInput = new QLineEdit(this);            // 输入消息文本框
    buttonSend = new QPushButton("发送消息", this);   // 发送按钮
    plainTextDisplay = new QPlainTextEdit(this);     // 消息显示区域
    plainTextDisplay->setReadOnly(true);             // 设置为只读显示

    // 获取本地 IP 地址并添加到下拉框
    comboBoxIp->addItem(getLocalIpAddress());

    // 设置界面布局
    QVBoxLayout *mainLayout = new QVBoxLayout;        // 主垂直布局
    QHBoxLayout *topLayout = new QHBoxLayout;         // 顶部水平布局（IP、端口、按钮）
    topLayout->addWidget(new QLabel("服务器地址："));  // 标签
    topLayout->addWidget(comboBoxIp);                 // IP下拉框
    topLayout->addWidget(new QLabel("服务器端口："));  // 标签
    topLayout->addWidget(spinBoxPort);                // 端口输入
    topLayout->addWidget(buttonStart);                // 启动按钮
    topLayout->addWidget(buttonStop);                 // 停止按钮

    QHBoxLayout *sendLayout = new QHBoxLayout;        // 消息发送区域布局
    sendLayout->addWidget(lineEditInput);             // 输入框
    sendLayout->addWidget(buttonSend);                // 发送按钮

    // 把布局加入主布局中
    mainLayout->addLayout(topLayout);
    mainLayout->addLayout(sendLayout);
    mainLayout->addWidget(plainTextDisplay);          // 添加消息显示区域

    central->setLayout(mainLayout);                   // 设置窗口主布局
    resize(720, 500);                                 // 设置窗口大小
    setWindowTitle("TCP服务器端 -- 纯代码实现");        // 设置窗口标题

    // 信号和槽连接
    connect(buttonStart, &QPushButton::clicked, this, &MainWindow::onStartClicked);   // 启动按钮
    connect(buttonStop, &QPushButton::clicked, this, &MainWindow::onStopClicked);     // 停止按钮
    connect(buttonSend, &QPushButton::clicked, this, &MainWindow::onSendClicked);     // 发送按钮
    connect(tcpServer, &QTcpServer::newConnection, this, &MainWindow::newConnection); // 新客户端连接
}

// 析构函数
MainWindow::~MainWindow() {}

// 获取本机的 IPv4 地址
QString MainWindow::getLocalIpAddress()
{
    QString localIp;
    const QString hostName = QHostInfo::localHostName();           // 获取主机名
    const QHostInfo info = QHostInfo::fromName(hostName);          // 获取主机地址信息

    for (const QHostAddress &addr : info.addresses()) {            // 遍历所有地址
        if (addr.protocol() == QAbstractSocket::IPv4Protocol) {    // 只选择 IPv4 地址
            localIp = addr.toString();                             // 转为字符串
            break;
        }
    }
    return localIp;
}

// 启动服务监听
void MainWindow::onStartClicked()
{
    QHostAddress address(comboBoxIp->currentText());  // 获取选中的 IP 地址
    quint16 port = spinBoxPort->value();              // 获取端口号

    if (tcpServer->listen(address, port)) {           // 开始监听
        plainTextDisplay->appendPlainText("✅ 开始监听服务器地址: " + address.toString() + ", 端口: " + QString::number(port));
        buttonStart->setEnabled(false);               // 禁用“启动”按钮
        buttonStop->setEnabled(true);                 // 启用“停止”按钮
    } else {
        // 监听失败，显示错误信息
        plainTextDisplay->appendPlainText("❌ 启动失败: " + tcpServer->errorString());
    }
}

// 停止服务器监听
void MainWindow::onStopClicked()
{
    if (tcpServer->isListening()) {
        tcpServer->close();                           // 关闭监听
        plainTextDisplay->appendPlainText("🛑 服务器已停止监听");
        buttonStart->setEnabled(true);                // 启用“启动”按钮
        buttonStop->setEnabled(false);                // 禁用“停止”按钮
    }
}

// 发送消息给客户端
void MainWindow::onSendClicked()
{
    if (!tcpSocket) return;                           // 若未连接客户端，直接返回

    QString message = lineEditInput->text().trimmed(); // 获取输入消息
    if (message.isEmpty()) return;                    // 如果为空则忽略

    plainTextDisplay->appendPlainText("[out] " + message); // 显示到界面
    lineEditInput->clear();                            // 清空输入框

    QByteArray data = message.toUtf8();                // 编码为字节数组
    data.append('\n');                                 // 添加换行符，便于识别
    tcpSocket->write(data);                            // 通过 socket 发送数据
}

// 有客户端连接时触发
void MainWindow::newConnection()
{
    tcpSocket = tcpServer->nextPendingConnection(); // 获取客户端连接 socket

    connect(tcpSocket, &QTcpSocket::readyRead, this, &MainWindow::socketReadData);      // 接收数据
    connect(tcpSocket, &QTcpSocket::disconnected, this, &MainWindow::clientDisconnected); // 客户端断开连接

    clientConnected(); // 显示客户端连接信息
}

// 读取客户端发来的数据
void MainWindow::socketReadData()
{
    while (tcpSocket->canReadLine()) {               // 当可以读取一行数据时
        QString line = tcpSocket->readLine().trimmed(); // 读取并清除换行
        plainTextDisplay->appendPlainText("[in] " + line); // 显示到界面
    }
}

// 显示客户端连接信息
void MainWindow::clientConnected()
{
    plainTextDisplay->appendPlainText("🔌 客户端已连接：");
    plainTextDisplay->appendPlainText("地址: " + tcpSocket->peerAddress().toString()); // 客户端IP
    plainTextDisplay->appendPlainText("端口: " + QString::number(tcpSocket->peerPort())); // 客户端端口
}
// 处理客户端断开连接
void MainWindow::clientDisconnected()
{
    plainTextDisplay->appendPlainText("❗ 客户端断开连接"); // 提示断开
    tcpSocket->deleteLater();                            // 延迟删除 socket
    tcpSocket = nullptr;                                 // 清空指针
}
// 主窗口关闭时执行
void MainWindow::closeEvent(QCloseEvent *event)
{
    if (tcpServer->isListening())
        tcpServer->close(); // 若在监听，关闭服务

    QMainWindow::closeEvent(event); // 调用父类处理逻辑
}
